/*
 * Copyright © 2024-2025 Wenze Wei
 *
 * This file is part of StadionOS.
 *
 * This Source Code Form is subject to the terms of the GNU General Public License, v. 2.0.
 * You can obtain a copy of the GPL at
 * https://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 */

#include "sys/network.h"
#include "../include/malloc.h"
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>

struct hostent *Sgethostbyname(const char *name) {
    static struct hostent host;
    static char *h_addr_list[2];
    static char h_addr[4];

    struct addrinfo hints, *res;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;

    if (Sgetaddrinfo(name, NULL, &hints, &res) != 0) {
        errno = ENOENT;
        return NULL;
    }

    struct sockaddr_in *addr = (struct sockaddr_in *)res->ai_addr;
    memcpy(h_addr, &addr->sin_addr, sizeof(addr->sin_addr));

    host.h_name = (char *)name;
    host.h_aliases = NULL;
    host.h_addrtype = AF_INET;
    host.h_length = sizeof(struct in_addr);
    host.h_addr_list = h_addr_list;
    h_addr_list[0] = h_addr;
    h_addr_list[1] = NULL;

    Sfreeaddrinfo(res);
    return &host;
}

struct hostent *Sgethostbyaddr(const void *addr, socklen_t len, int type) {
    static struct hostent host;
    static char *h_addr_list[2];
    static char h_name[NI_MAXHOST];

    if (type != AF_INET || len != sizeof(struct in_addr)) {
        errno = EAFNOSUPPORT;
        return NULL;
    }

    if (getnameinfo((struct sockaddr *)addr, len, h_name, sizeof(h_name), NULL, 0, 0) != 0) {
        errno = ENOENT;
        return NULL;
    }

    host.h_name = h_name;
    host.h_aliases = NULL;
    host.h_addrtype = AF_INET;
    host.h_length = sizeof(struct in_addr);
    host.h_addr_list = h_addr_list;
    h_addr_list[0] = (char *)addr;
    h_addr_list[1] = NULL;

    return &host;
}

void Sfreeaddrinfo(struct addrinfo *ai) {
    while (ai) {
        struct addrinfo *next = ai->ai_next;
        free(ai->ai_addr);
        free(ai->ai_canonname);
        free(ai);
        ai = next;
    }
}

int Sgetaddrinfo(const char *node, const char *service,
               const struct addrinfo *hints,
               struct addrinfo **res) {
    struct addrinfo *ai;
    struct sockaddr_in *sin;

    ai = (struct addrinfo *)malloc(sizeof(struct addrinfo));
    if (!ai) return EAI_MEMORY;

    sin = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
    if (!sin) {
        free(ai);
        return EAI_MEMORY;
    }

    memset(sin, 0, sizeof(struct sockaddr_in));
    sin->sin_family = AF_INET;
    sin->sin_port = htons(atoi(service));
    Sinet_pton(AF_INET, node, &sin->sin_addr);

    ai->ai_family = AF_INET;
    ai->ai_socktype = hints->ai_socktype;
    ai->ai_protocol = hints->ai_protocol;
    ai->ai_addrlen = sizeof(struct sockaddr_in);
    ai->ai_addr = (struct sockaddr *)sin;
    ai->ai_canonname = NULL;
    ai->ai_next = NULL;

    *res = ai;
    return 0;
}
